﻿using System;
using System.Data;
using System.Data.SqlClient;

namespace Dx.SqlAccess
{
    public sealed class QueryParameter : MarshalByRefObject, IDbDataParameter, IDataParameter, ICloneable
    {
        private System.Data.DbType _dbType;
        private ParameterDirection _direction;
        private bool _forceSize;
        private bool _isNullable;
        private string _name;
        private int _offset;
        private byte _precision;
        private IDbDataParameter _realParameter;
        private byte _scale;
        private int _size;
        private string _sourceColumn;
        private bool _suppress;
        private object _value;
        private DataRowVersion _version;

        public QueryParameter()
        {
            this._value = null;
            this._direction = ParameterDirection.Input;
            this._size = -1;
            this._version = DataRowVersion.Current;
            this._forceSize = false;
            this._offset = 0;
            this._suppress = false;
        }

        public QueryParameter(string parameterName, System.Data.DbType dbType) : this()
        {
            this._name = parameterName;
            this._dbType = dbType;
        }

        public QueryParameter(string parameterName, object Value) : this()
        {
            this._name = parameterName;
            this._value = Value;
        }

        public QueryParameter(string parameterName, System.Data.DbType dbType, int size) : this()
        {
            this._name = parameterName;
            this._dbType = dbType;
            this._size = size;
        }

        public QueryParameter(string parameterName, object Value, System.Data.DbType dbType) : this()
        {
            this._name = parameterName;
            this._dbType = dbType;
            this._value = Value;
        }

        public QueryParameter(string parameterName, System.Data.DbType dbType, int size, string sourceColumn) : this()
        {
            this._name = parameterName;
            this._dbType = dbType;
            this._size = size;
            this._sourceColumn = sourceColumn;
        }

        public QueryParameter(string parameterName, System.Data.DbType dbType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string sourceColumn, DataRowVersion sourceVersion, object Value) : this()
        {
            this._name = parameterName;
            this._dbType = dbType;
            this._size = size;
            this._direction = direction;
            this._isNullable = isNullable;
            this._precision = precision;
            this._scale = scale;
            this._sourceColumn = sourceColumn;
            this._version = sourceVersion;
            this._value = Value;
        }

        public object Clone()
        {
            QueryParameter parameter = new QueryParameter();
            parameter.SetProperties(this._name, this._sourceColumn, this._version, this._precision, this._scale, this._size, this._forceSize, this._offset, this._direction, this._value, this._dbType, this._suppress);
            return parameter;
        }

        internal void InitRealParameter()
        {
            if (object.Equals(this._realParameter, null))
            {
                this._realParameter = new SqlParameter();
            }
            this.RealParameter.DbType = this.DbType;
            this.RealParameter.Direction = this.Direction;
            this.RealParameter.ParameterName = this.ParameterName;
            this.RealParameter.Precision = this.Precision;
            this.RealParameter.Scale = this.Scale;
            this.RealParameter.Size = this.Size;
            this.RealParameter.SourceColumn = this.SourceColumn;
            this.RealParameter.SourceVersion = this.SourceVersion;
            this.RealParameter.Value = this.Value;
        }

        internal void SetProperties(string name, string column, DataRowVersion version, byte precision, byte scale, int size, bool forceSize, int offset, ParameterDirection direction, object Value, System.Data.DbType type, bool suppress)
        {
            this.ParameterName = name;
            this._sourceColumn = column;
            this.SourceVersion = version;
            this.Precision = precision;
            this._scale = scale;
            this._size = size;
            this._forceSize = forceSize;
            this._offset = offset;
            this.Direction = direction;
            if (Value is ICloneable)
            {
                Value = ((ICloneable) Value).Clone();
            }
            this._value = Value;
            this.Suppress = suppress;
        }

        internal void SyncParameter()
        {
            if (!object.Equals(this._realParameter, null))
            {
                this.SetProperties(this.RealParameter.ParameterName, this.RealParameter.SourceColumn, this.RealParameter.SourceVersion, this.RealParameter.Precision, this.RealParameter.Scale, this.RealParameter.Size, this._forceSize, this._offset, this.RealParameter.Direction, this.RealParameter.Value, this.RealParameter.DbType, this.Suppress);
            }
        }

        public override string ToString()
        {
            return this.ParameterName;
        }

        public System.Data.DbType DbType
        {
            get
            {
                return this._dbType;
            }
            set
            {
                this._dbType = value;
            }
        }

        public ParameterDirection Direction
        {
            get
            {
                return this._direction;
            }
            set
            {
                this._direction = value;
            }
        }

        public bool IsNullable
        {
            get
            {
                return this._isNullable;
            }
            set
            {
                this._isNullable = value;
            }
        }

        public int Offset
        {
            get
            {
                return this._offset;
            }
            set
            {
                this._offset = value;
            }
        }

        public string ParameterName
        {
            get
            {
                return this._name;
            }
            set
            {
                this._name = value;
            }
        }

        public byte Precision
        {
            get
            {
                return this._precision;
            }
            set
            {
                this._precision = value;
            }
        }

        internal IDbDataParameter RealParameter
        {
            get
            {
                return this._realParameter;
            }
        }

        public byte Scale
        {
            get
            {
                return this._scale;
            }
            set
            {
                this._scale = value;
            }
        }

        public int Size
        {
            get
            {
                if (this._forceSize)
                {
                    return this._size;
                }
                return 0;
            }
            set
            {
                if (value < 0)
                {
                    throw new Exception(value.ToString());
                }
                if (value != 0)
                {
                    this._forceSize = true;
                    this._size = value;
                }
                else
                {
                    this._forceSize = false;
                    this._size = -1;
                }
            }
        }

        public string SourceColumn
        {
            get
            {
                if (this._sourceColumn == null)
                {
                    return string.Empty;
                }
                return this._sourceColumn;
            }
            set
            {
                this._sourceColumn = value;
            }
        }

        public DataRowVersion SourceVersion
        {
            get
            {
                return this._version;
            }
            set
            {
                this._version = value;
            }
        }

        internal bool Suppress
        {
            get
            {
                return this._suppress;
            }
            set
            {
                this._suppress = value;
            }
        }

        public object Value
        {
            get
            {
                if (object.Equals(this._value, null))
                {
                    return DBNull.Value;
                }
                return this._value;
            }
            set
            {
                this._value = value;
            }
        }
    }
}

